/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package parser.table;

import java.util.LinkedList;
import java.util.List;
import structures.grammar.Grammar;
import structures.grammar.Rule;
import structures.table.Situation;
import structures.table.SituationSet;

/**
 *
 * @author Slayer
 */
public class LALRTableBuilder extends TableBuilder{
    
    private List<List<Integer>> mergeGroups;
    private List<Core> cores;

    public LALRTableBuilder(Grammar grammar) {
        super(grammar);
    }
    
    @Override
    protected int getStateNumber(List<SituationSet> list, SituationSet set) {
        Core core = getCore(set);
        int coresPos = cores.indexOf(core);
        
        for (int index = 0; index < mergeGroups.size(); ++index) {
            List<Integer> group = mergeGroups.get(index);
            
            if (group.contains(coresPos)) {
                return index;
            }
        }
        
        return -1;
    }
    
    //<editor-fold defaultstate="collapsed" desc="Class core for holding cores of SituationSet">
    private class Core {
        

        public List<Rule> coreRules;
        public List<Integer> corePoints;

        public Core(List<Rule> coreRules, List<Integer> corePoints) {
            this.coreRules = coreRules;
            this.corePoints = corePoints;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Core other = (Core) obj;
            if (this.coreRules != other.coreRules && (this.coreRules == null || !this.coreRules.equals(other.coreRules))) {
                return false;
            }
            if (this.corePoints != other.corePoints && (this.corePoints == null || !this.corePoints.equals(other.corePoints))) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 3;
            hash = 79 * hash + (this.coreRules != null ? this.coreRules.hashCode() : 0);
            hash = 79 * hash + (this.corePoints != null ? this.corePoints.hashCode() : 0);
            return hash;
        }
    }
    //</editor-fold>

    private Core getCore(SituationSet set) {
        List<Rule> coreRules = new LinkedList<Rule>();
        List<Integer> corePoints = new LinkedList<Integer>();

        for (Situation situation : set) {
            Rule rule = situation.getRule();

            if (!coreRules.contains(rule)) {
                coreRules.add(rule);
                corePoints.add(situation.getPointPosition());
            }
        }

        return new Core(coreRules, corePoints);
    }

    @Override
    protected List<SituationSet> items() {
        List<SituationSet> items = super.items();
        List<Core> uniqueCores = new LinkedList<Core>();

        mergeGroups = new LinkedList<List<Integer>>();
        cores = new LinkedList<Core>();
        
        //get unique and all cores
        for (SituationSet set : items) {
            Core core = getCore(set);
            
            cores.add(core);
            
            if (!uniqueCores.contains(core)) {
                uniqueCores.add(core);
            }
        }
        
        //init mergeGroups
        for (int i = 0; i < uniqueCores.size(); ++i) {
            mergeGroups.add(new LinkedList<Integer>());
        }
        
        for (int index = 0; index < cores.size(); ++index) {
            Core core = cores.get(index);
            int groupNum = uniqueCores.indexOf(core);
            
            mergeGroups.get(groupNum).add(index);
        }
        
        List<SituationSet> result = new LinkedList<SituationSet>();
        
        //merge
        for (List<Integer> group : mergeGroups) {
            SituationSet set = items.get(group.get(0));
            
            for (int index = 1; index < group.size(); ++index) {
                set.addAll(items.get(group.get(index)));
            }
            
            result.add(set);
        }
        
        return result;
    }
}
